# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\sqlalchemy\sql\default_comparator.py
"""Default implementation of SQL comparison operations.
"""
from .. import exc, util
from . import type_api
from . import operators
from .elements import (
    BindParameter,
    True_,
    False_,
    BinaryExpression,
    Null,
    _const_expr,
    _clause_element_as_expr,
    ClauseList,
    ColumnElement,
    TextClause,
    UnaryExpression,
    collate,
    _is_literal,
    _literal_as_text,
    ClauseElement,
    and_,
    or_,
    Slice,
    Visitable,
    _literal_as_binds,
    CollectionAggregate,
)
from .selectable import SelectBase, Alias, Selectable, ScalarSelect


def _boolean_compare(
    expr,
    op,
    obj,
    negate=None,
    reverse=False,
    _python_is_types=(util.NoneType, bool),
    result_type=None,
    **kwargs
):
    if result_type is None:
        result_type = type_api.BOOLEANTYPE
    if isinstance(obj, _python_is_types + (Null, True_, False_)):
        if op in (operators.eq, operators.ne):
            if isinstance(obj, (bool, True_, False_)):
                return BinaryExpression(
                    expr,
                    _literal_as_text(obj),
                    op,
                    type_=result_type,
                    negate=negate,
                    modifiers=kwargs,
                )
            if op in (operators.is_distinct_from, operators.isnot_distinct_from):
                return BinaryExpression(
                    expr,
                    _literal_as_text(obj),
                    op,
                    type_=result_type,
                    negate=negate,
                    modifiers=kwargs,
                )
            if op in (operators.eq, operators.is_):
                return BinaryExpression(
                    expr,
                    _const_expr(obj),
                    operators.is_,
                    negate=operators.isnot,
                    type_=result_type,
                )
            if op in (operators.ne, operators.isnot):
                return BinaryExpression(
                    expr,
                    _const_expr(obj),
                    operators.isnot,
                    negate=operators.is_,
                    type_=result_type,
                )
            raise exc.ArgumentError(
                "Only '=', '!=', 'is_()', 'isnot()', 'is_distinct_from()', 'isnot_distinct_from()' operators can be used with None/True/False"
            )
        else:
            obj = _check_literal(expr, op, obj)
        if reverse:
            return BinaryExpression(
                obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs
            )
        return BinaryExpression(
            expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs
        )


def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw):
    if result_type is None:
        if op.return_type:
            result_type = op.return_type
        else:
            if op.is_comparison:
                result_type = type_api.BOOLEANTYPE
        return _binary_operate(reverse=reverse, result_type=result_type, **kw)


def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
    obj = _check_literal(expr, op, obj)
    if reverse:
        left, right = obj, expr
    else:
        left, right = expr, obj
    if result_type is None:
        op, result_type = left.comparator._adapt_expression(op, right.comparator)
    return BinaryExpression(left, right, op, type_=result_type, modifiers=kw)


def _conjunction_operate(expr, op, other, **kw):
    if op is operators.and_:
        return and_(expr, other)
    if op is operators.or_:
        return or_(expr, other)
    raise NotImplementedError()


def _scalar(expr, op, fn, **kw):
    return fn(expr)


def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
    seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
    if isinstance(seq_or_selectable, ScalarSelect):
        return _boolean_compare(expr, op, seq_or_selectable, negate=negate_op)
    if isinstance(seq_or_selectable, SelectBase):
        return _boolean_compare(negate=negate_op, **kw)
    if isinstance(seq_or_selectable, (Selectable, TextClause)):
        return _boolean_compare(negate=negate_op, **kw)
    if isinstance(seq_or_selectable, ClauseElement):
        if isinstance(seq_or_selectable, BindParameter):
            if seq_or_selectable.expanding:
                return _boolean_compare(expr, op, seq_or_selectable, negate=negate_op)
            raise exc.InvalidRequestError(
                'in_() accepts either a list of expressions, a selectable, or an "expanding" bound parameter: %r'
                % seq_or_selectable
            )
        args = []
        for o in seq_or_selectable:
            if not _is_literal(o):
                if not isinstance(o, operators.ColumnOperators):
                    raise exc.InvalidRequestError(
                        'in_() accepts either a list of expressions, a selectable, or an "expanding" bound parameter: %r'
                        % o
                    )
            else:
                if o is None:
                    o = Null()
                else:
                    o = expr._bind_param(op, o)
                args.append(o)

        if len(args) == 0:
            op, negate_op = (
                (operators.empty_in_op, operators.empty_notin_op)
                if op is operators.in_op
                else (operators.empty_notin_op, operators.empty_in_op)
            )
        return _boolean_compare(
            expr, op, (ClauseList(*args)).self_group(against=op), negate=negate_op
        )


def _getitem_impl(expr, op, other, **kw):
    if isinstance(expr.type, type_api.INDEXABLE):
        other = _check_literal(expr, op, other)
        return _binary_operate(expr, op, other, **kw)
    _unsupported_impl(expr, op, other, **kw)


def _unsupported_impl(expr, op, *arg, **kw):
    raise NotImplementedError(
        "Operator '%s' is not supported on this expression" % op.__name__
    )


def _inv_impl(expr, op, **kw):
    """See :meth:`.ColumnOperators.__inv__`."""
    if hasattr(expr, "negation_clause"):
        return expr.negation_clause
    else:
        return expr._negate()


def _neg_impl(expr, op, **kw):
    """See :meth:`.ColumnOperators.__neg__`."""
    return UnaryExpression(expr, operator=operators.neg, type_=expr.type)


def _match_impl(expr, op, other, **kw):
    """See :meth:`.ColumnOperators.match`."""
    return _boolean_compare(
        result_type=type_api.MATCHTYPE,
        negate=operators.notmatch_op
        if op is operators.match_op
        else operators.match_op,
        **kw
    )


def _distinct_impl(expr, op, **kw):
    """See :meth:`.ColumnOperators.distinct`."""
    return UnaryExpression(expr, operator=operators.distinct_op, type_=expr.type)


def _between_impl(expr, op, cleft, cright, **kw):
    """See :meth:`.ColumnOperators.between`."""
    return BinaryExpression(
        expr,
        ClauseList(
            _check_literal(expr, operators.and_, cleft),
            _check_literal(expr, operators.and_, cright),
            operator=operators.and_,
            group=False,
            group_contents=False,
        ),
        op,
        negate=operators.notbetween_op
        if op is operators.between_op
        else operators.between_op,
        modifiers=kw,
    )


def _collate_impl(expr, op, other, **kw):
    return collate(expr, other)


operator_lookup = {
    "and_": (_conjunction_operate,),
    "or_": (_conjunction_operate,),
    "inv": (_inv_impl,),
    "add": (_binary_operate,),
    "mul": (_binary_operate,),
    "sub": (_binary_operate,),
    "div": (_binary_operate,),
    "mod": (_binary_operate,),
    "truediv": (_binary_operate,),
    "custom_op": (_custom_op_operate,),
    "json_path_getitem_op": (_binary_operate,),
    "json_getitem_op": (_binary_operate,),
    "concat_op": (_binary_operate,),
    "any_op": (_scalar, CollectionAggregate._create_any),
    "all_op": (_scalar, CollectionAggregate._create_all),
    "lt": (_boolean_compare, operators.ge),
    "le": (_boolean_compare, operators.gt),
    "ne": (_boolean_compare, operators.eq),
    "gt": (_boolean_compare, operators.le),
    "ge": (_boolean_compare, operators.lt),
    "eq": (_boolean_compare, operators.ne),
    "is_distinct_from": (_boolean_compare, operators.isnot_distinct_from),
    "isnot_distinct_from": (_boolean_compare, operators.is_distinct_from),
    "like_op": (_boolean_compare, operators.notlike_op),
    "ilike_op": (_boolean_compare, operators.notilike_op),
    "notlike_op": (_boolean_compare, operators.like_op),
    "notilike_op": (_boolean_compare, operators.ilike_op),
    "contains_op": (_boolean_compare, operators.notcontains_op),
    "startswith_op": (_boolean_compare, operators.notstartswith_op),
    "endswith_op": (_boolean_compare, operators.notendswith_op),
    "desc_op": (_scalar, UnaryExpression._create_desc),
    "asc_op": (_scalar, UnaryExpression._create_asc),
    "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst),
    "nullslast_op": (_scalar, UnaryExpression._create_nullslast),
    "in_op": (_in_impl, operators.notin_op),
    "notin_op": (_in_impl, operators.in_op),
    "is_": (_boolean_compare, operators.is_),
    "isnot": (_boolean_compare, operators.isnot),
    "collate": (_collate_impl,),
    "match_op": (_match_impl,),
    "notmatch_op": (_match_impl,),
    "distinct_op": (_distinct_impl,),
    "between_op": (_between_impl,),
    "notbetween_op": (_between_impl,),
    "neg": (_neg_impl,),
    "getitem": (_getitem_impl,),
    "lshift": (_unsupported_impl,),
    "rshift": (_unsupported_impl,),
    "contains": (_unsupported_impl,),
}


def _check_literal(expr, operator, other, bindparam_type=None):
    if isinstance(other, (ColumnElement, TextClause)):
        if isinstance(other, BindParameter):
            if other.type._isnull:
                other = other._clone()
                other.type = expr.type
        return other
    if hasattr(other, "__clause_element__"):
        other = other.__clause_element__()
    else:
        if isinstance(other, type_api.TypeEngine.Comparator):
            other = other.expr
    if isinstance(other, (SelectBase, Alias)):
        return other.as_scalar()
    elif not isinstance(other, Visitable):
        return expr._bind_param(operator, other, type_=bindparam_type)
    else:
        return other
